/*
 * Copyright 2011 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.core.utils
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public class HashUtils
	{

		private static const CHARS:Vector.<uint> = new Vector.<uint>([0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66]);

		public static function computeMD5(bytes:ByteArray):ByteArray
		{
			var blocks:Vector.<int> = createBlocks(bytes);
			var len:int = blocks.length;
			var a:int = 1732584193;
			var b:int = -271733879;
			var c:int = -1732584194;
			var d:int = 271733878;
			var t:int, aa:int, bb:int, cc:int, dd:int;

			for (var i:int = 0; i < len; i += 16)
			{
				aa = a;
				bb = b;
				cc = c;
				dd = d;

				a = (((t = a + ((b & c) | ((~b) & d)) + blocks[i] - 680876936) << 7) | (t >>> 25)) + b;
				d = (((t = d + ((a & b) | ((~a) & c)) + blocks[i + 1] - 389564586) << 12) | (t >>> 20)) + a;
				c = (((t = c + ((d & a) | ((~d) & b)) + blocks[i + 2] + 606105819) << 17) | (t >>> 15)) + d;
				b = (((t = b + ((c & d) | ((~c) & a)) + blocks[i + 3] - 1044525330) << 22) | (t >>> 10)) + c;
				a = (((t = a + ((b & c) | ((~b) & d)) + blocks[i + 4] - 176418897) << 7) | (t >>> 25)) + b;
				d = (((t = d + ((a & b) | ((~a) & c)) + blocks[i + 5] + 1200080426) << 12) | (t >>> 20)) + a;
				c = (((t = c + ((d & a) | ((~d) & b)) + blocks[i + 6] - 1473231341) << 17) | (t >>> 15)) + d;
				b = (((t = b + ((c & d) | ((~c) & a)) + blocks[i + 7] - 45705983) << 22) | (t >>> 10)) + c;
				a = (((t = a + ((b & c) | ((~b) & d)) + blocks[i + 8] + 1770035416) << 7) | (t >>> 25)) + b;
				d = (((t = d + ((a & b) | ((~a) & c)) + blocks[i + 9] - 1958414417) << 12) | (t >>> 20)) + a;
				c = (((t = c + ((d & a) | ((~d) & b)) + blocks[i + 10] - 42063) << 17) | (t >>> 15)) + d;
				b = (((t = b + ((c & d) | ((~c) & a)) + blocks[i + 11] - 1990404162) << 22) | (t >>> 10)) + c;
				a = (((t = a + ((b & c) | ((~b) & d)) + blocks[i + 12] + 1804603682) << 7) | (t >>> 25)) + b;
				d = (((t = d + ((a & b) | ((~a) & c)) + blocks[i + 13] - 40341101) << 12) | (t >>> 20)) + a;
				c = (((t = c + ((d & a) | ((~d) & b)) + blocks[i + 14] - 1502002290) << 17) | (t >>> 15)) + d;
				b = (((t = b + ((c & d) | ((~c) & a)) + blocks[i + 15] + 1236535329) << 22) | (t >>> 10)) + c;

				a = (((t = a + ((b & d) | (c & (~d))) + blocks[i + 1] - 165796510) << 5) | (t >>> 27)) + b;
				d = (((t = d + ((a & c) | (b & (~c))) + blocks[i + 6] - 1069501632) << 9) | (t >>> 23)) + a;
				c = (((t = c + ((d & b) | (a & (~b))) + blocks[i + 11] + 643717713) << 14) | (t >>> 18)) + d;
				b = (((t = b + ((c & a) | (d & (~a))) + blocks[i] - 373897302) << 20) | (t >>> 12)) + c;
				a = (((t = a + ((b & d) | (c & (~d))) + blocks[i + 5] - 701558691) << 5) | (t >>> 27)) + b;
				d = (((t = d + ((a & c) | (b & (~c))) + blocks[i + 10] + 38016083) << 9) | (t >>> 23)) + a;
				c = (((t = c + ((d & b) | (a & (~b))) + blocks[i + 15] - 660478335) << 14) | (t >>> 18)) + d;
				b = (((t = b + ((c & a) | (d & (~a))) + blocks[i + 4] - 405537848) << 20) | (t >>> 12)) + c;
				a = (((t = a + ((b & d) | (c & (~d))) + blocks[i + 9] + 568446438) << 5) | (t >>> 27)) + b;
				d = (((t = d + ((a & c) | (b & (~c))) + blocks[i + 14] - 1019803690) << 9) | (t >>> 23)) + a;
				c = (((t = c + ((d & b) | (a & (~b))) + blocks[i + 3] - 187363961) << 14) | (t >>> 18)) + d;
				b = (((t = b + ((c & a) | (d & (~a))) + blocks[i + 8] + 1163531501) << 20) | (t >>> 12)) + c;
				a = (((t = a + ((b & d) | (c & (~d))) + blocks[i + 13] - 1444681467) << 5) | (t >>> 27)) + b;
				d = (((t = d + ((a & c) | (b & (~c))) + blocks[i + 2] - 51403784) << 9) | (t >>> 23)) + a;
				c = (((t = c + ((d & b) | (a & (~b))) + blocks[i + 7] + 1735328473) << 14) | (t >>> 18)) + d;
				b = (((t = b + ((c & a) | (d & (~a))) + blocks[i + 12] - 1926607734) << 20) | (t >>> 12)) + c;

				a = (((t = a + (b ^ c ^ d) + blocks[i + 5] - 378558) << 4) | (t >>> 28)) + b;
				d = (((t = d + (a ^ b ^ c) + blocks[i + 8] - 2022574463) << 11) | (t >>> 21)) + a;
				c = (((t = c + (d ^ a ^ b) + blocks[i + 11] + 1839030562) << 16) | (t >>> 16)) + d;
				b = (((t = b + (c ^ d ^ a) + blocks[i + 14] - 35309556) << 23) | (t >>> 9)) + c;
				a = (((t = a + (b ^ c ^ d) + blocks[i + 1] - 1530992060) << 4) | (t >>> 28)) + b;
				d = (((t = d + (a ^ b ^ c) + blocks[i + 4] + 1272893353) << 11) | (t >>> 21)) + a;
				c = (((t = c + (d ^ a ^ b) + blocks[i + 7] - 155497632) << 16) | (t >>> 16)) + d;
				b = (((t = b + (c ^ d ^ a) + blocks[i + 10] - 1094730640) << 23) | (t >>> 9)) + c;
				a = (((t = a + (b ^ c ^ d) + blocks[i + 13] + 681279174) << 4) | (t >>> 28)) + b;
				d = (((t = d + (a ^ b ^ c) + blocks[i] - 358537222) << 11) | (t >>> 21)) + a;
				c = (((t = c + (d ^ a ^ b) + blocks[i + 3] - 722521979) << 16) | (t >>> 16)) + d;
				b = (((t = b + (c ^ d ^ a) + blocks[i + 6] + 76029189) << 23) | (t >>> 9)) + c;
				a = (((t = a + (b ^ c ^ d) + blocks[i + 9] - 640364487) << 4) | (t >>> 28)) + b;
				d = (((t = d + (a ^ b ^ c) + blocks[i + 12] - 421815835) << 11) | (t >>> 21)) + a;
				c = (((t = c + (d ^ a ^ b) + blocks[i + 15] + 530742520) << 16) | (t >>> 16)) + d;
				b = (((t = b + (c ^ d ^ a) + blocks[i + 2] - 995338651) << 23) | (t >>> 9)) + c;

				a = (((t = a + (c ^ (b | (~d))) + blocks[i] - 198630844) << 6) | (t >>> 26)) + b;
				d = (((t = d + (b ^ (a | (~c))) + blocks[i + 7] + 1126891415) << 10) | (t >>> 22)) + a;
				c = (((t = c + (a ^ (d | (~b))) + blocks[i + 14] - 1416354905) << 15) | (t >>> 17)) + d;
				b = (((t = b + (d ^ (c | (~a))) + blocks[i + 5] - 57434055) << 21) | (t >>> 11)) + c;
				a = (((t = a + (c ^ (b | (~d))) + blocks[i + 12] + 1700485571) << 6) | (t >>> 26)) + b;
				d = (((t = d + (b ^ (a | (~c))) + blocks[i + 3] - 1894986606) << 10) | (t >>> 22)) + a;
				c = (((t = c + (a ^ (d | (~b))) + blocks[i + 10] - 1051523) << 15) | (t >>> 17)) + d;
				b = (((t = b + (d ^ (c | (~a))) + blocks[i + 1] - 2054922799) << 21) | (t >>> 11)) + c;
				a = (((t = a + (c ^ (b | (~d))) + blocks[i + 8] + 1873313359) << 6) | (t >>> 26)) + b;
				d = (((t = d + (b ^ (a | (~c))) + blocks[i + 15] - 30611744) << 10) | (t >>> 22)) + a;
				c = (((t = c + (a ^ (d | (~b))) + blocks[i + 6] - 1560198380) << 15) | (t >>> 17)) + d;
				b = (((t = b + (d ^ (c | (~a))) + blocks[i + 13] + 1309151649) << 21) | (t >>> 11)) + c;
				a = (((t = a + (c ^ (b | (~d))) + blocks[i + 4] - 145523070) << 6) | (t >>> 26)) + b;
				d = (((t = d + (b ^ (a | (~c))) + blocks[i + 11] - 1120210379) << 10) | (t >>> 22)) + a;
				c = (((t = c + (a ^ (d | (~b))) + blocks[i + 2] + 718787259) << 15) | (t >>> 17)) + d;
				b = (((t = b + (d ^ (c | (~a))) + blocks[i + 9] - 343485551) << 21) | (t >>> 11)) + c;

				a += aa;
				b += bb;
				c += cc;
				d += dd;
			}

			var digest:ByteArray = new ByteArray();
			digest.endian = Endian.LITTLE_ENDIAN;
			digest.writeInt(a);
			digest.writeInt(b);
			digest.writeInt(c);
			digest.writeInt(d);
			digest.position = 0;

			return digest;
		}

		public static function digestToString(bytes:ByteArray):String
		{
			var result:String = "";
			var big:Boolean = bytes.endian === Endian.BIG_ENDIAN;
			var c:Vector.<uint> = CHARS;
			var pos:uint = bytes.position;

			bytes.position = 0;

			for (var i:int = 0; i < bytes.length / 4; i++)
			{
				var n:int = bytes.readInt();

				if (big)
				{
					result += String.fromCharCode(c[(n >> 28) & 0xf], c[(n >> 24) & 0xf], c[(n >> 20) & 0xf], c[(n >> 16) & 0xf], c[(n >> 12) & 0xf], c[(n >> 8) & 0xf], c[(n >> 4) & 0xf], c[n & 0xf]);
				}
				else
				{
					result += String.fromCharCode(c[(n >> 4) & 0xf], c[n & 0xf], c[(n >> 12) & 0xf], c[(n >> 8) & 0xf], c[(n >> 20) & 0xf], c[(n >> 16) & 0xf], c[(n >> 28) & 0xf], c[(n >> 24) & 0xf]);
				}
			}

			bytes.position = pos;

			return result;
		}

		private static function createBlocks(bytes:ByteArray):Vector.<int>
		{
			var len:int = bytes.length * 8;
			var blocks:Vector.<int> = new Vector.<int>((((len + 64) >>> 9) << 4) + 16, true);

			for (var i:int = 0; i < len; i += 8)
			{
				blocks[int(i >> 5)] |= (bytes[i / 8] & 0xff) << (i % 32);
			}

			blocks[int(len >> 5)] |= 0x80 << (len % 32);
			blocks[int((((len + 64) >>> 9) << 4) + 14)] = len;

			return blocks;
		}
	}
}
